﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using CoreEx.Common.Extensions;
using CoreEx.Common.Proxy;
using LinFu.IoC;
using LinFu.IoC.Configuration.Interfaces;
using LinFu.IoC.Interfaces;
using LinFu.Proxy.Interfaces;
using LinFu.Reflection;

namespace CoreEx.Database.Client.Implementation
{    
    [LoaderPlugin]
    public class ProviderPlugin : IContainerPlugin
    {
        private bool _initialized;

        #region ILoaderPlugin<IServiceContainer> Members

        public void BeginLoad(IServiceContainer target)
        {
            if (_initialized)
                return;

            InjectProxyFactories(target);
        }

        private static void InjectProxyFactories(IServiceContainer target)
        {
            //Inject all proxy factory services
            target.Inject<IProxyFactory<IDbConnection>>().Using(() => new ConnectionProxyFactory()).AsSingleton();
            target.Inject<IProxyFactory<IDbTransaction>>().Using(() => new ProxyFactory<IDbTransaction>()).AsSingleton();
            target.Inject<IProxyFactory<IDbCommand>>().Using(() => new ProxyFactory<IDbCommand>()).AsSingleton();
            target.Inject<IProxyFactory<IDataReader>>().Using(() => new ProxyFactory<IDataReader>()).AsSingleton();
            target.Inject<IProxyFactory<DbProviderFactory>>().Using(() => new ProxyFactory<DbProviderFactory>()).
                AsSingleton();
        }


        public void EndLoad(IServiceContainer target)
        {
            if (_initialized)
                return;

            IEnumerable<IConnectionInfo> connections = GetAvailableConnections(target);

            InjectConnections(target, connections);

            InjectParameterCache(target, connections);

            //InjectDbProviderFactories(target, connections);

            _initialized = true;

            //var proxyFactory = target.GetService<IProxyFactory>();
            //proxyFactory.CreateProxyType(typeof(DbProviderFactory), new[] { typeof(IActualTarget) });
            //proxyFactory.CreateProxyType(typeof(IDbCommand), new[] { typeof(IActualTarget)});
            //proxyFactory.CreateProxyType(typeof(IDbConnection), new[] { typeof(IActualTarget), typeof(IDbConnectionInfo) });
        }

    

        private static void InjectParameterCache(IServiceContainer target, IEnumerable<IConnectionInfo> connections)
        {
            //Inject a parameter cache service for each named connection.
            connections.ForEach(
                c =>
                target.Inject<IParameterCache>(c.Name).Using(() => new ParameterCache())
                    .AsSingleton());
        }

        private static void InjectConnections(IServiceContainer target, IEnumerable<IConnectionInfo> connections)
        {
            //Inject all named connection info services
            connections.ForEach(s => target.Inject<IConnectionInfo>(s.Name)
                                         .Using(
                                         () =>
                                         new ConnectionInfo
                                             {
                                                 Name = s.Name,
                                                 ProviderName = s.ProviderName,
                                                 ConnectionString = s.ConnectionString
                                             }).AsSingleton());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private static IEnumerable<IConnectionInfo> GetAvailableConnections(IServiceContainer target)
        {
            var connectionInfoProvider = target.GetService<IConnectionInfoProvider>();
            return connectionInfoProvider.GetConnections();
        }

        #endregion
    }
}
